package cn.aitrox.ry.util;

import cn.aitrox.ry.constant.CommonConstant;
import com.google.common.base.Strings;
import com.google.common.collect.Maps;
import com.google.common.io.Files;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.zip.GZIPInputStream;

public class FsUtil {
    private final static Logger log = LoggerFactory.getLogger(FsUtil.class);
    private final static String Separator = "/"; // File.separator;
    public static final long FILE_MAX = 1024L * 1024 * 1024 * 1024;// unit byte

    public static RandomAccessFile randomOpen(String filePath, boolean write) throws IOException {
        return new RandomAccessFile(filePath, write ? "rw" : "r");
    }

    public static void randomAppend(RandomAccessFile raf, byte[] data) throws IOException {
        if (raf == null)
            return;
        long pos = raf.length();
        raf.seek(pos);
        raf.write(data);
    }

    public static void randomClose(RandomAccessFile raf) throws IOException {
        if (raf != null)
            raf.close();
    }

    public static byte[] randomRead(String filePath, long pos, int length) throws IOException {
        byte[] data = new byte[length];
        RandomAccessFile raf = new RandomAccessFile(filePath, "r");
        raf.seek(pos);
        raf.read(data, 0, length);
        raf.close();
        return data;
    }

    public static void randomAppend(String filePath, byte[] data) throws IOException {
        RandomAccessFile raf = new RandomAccessFile(filePath, "rw");
        long pos = raf.length();
        raf.seek(pos);
        raf.write(data);
        raf.close();
    }

    public static void deleteEmptyDir(File dir) {
        if (dir.isDirectory()) {
            File[] files = dir.listFiles();
            if (files == null)
                return;

            for (File file : files) {
                if (file != null && file.isDirectory()) {
                    deleteEmptyDir(file);
                    if (file.listFiles().length <= 0) {
                        file.delete();
                    }
                }
            }
        }
    }

    public static boolean moveFile(File srcFile, File destFile) {
        if (!srcFile.exists() || !srcFile.isFile())
            return false;

        ensurePathExist(destFile);
        return srcFile.renameTo(destFile);
    }

    public static void moveDir(File srcFile, File destFile) {
        if (srcFile.isDirectory()) {
            ensureDirExist(destFile);
            for (String file : srcFile.list()) {
                moveDir(new File(srcFile, file), new File(destFile, file));
            }
            srcFile.delete();
        } else {
            moveFile(srcFile, destFile);
        }
    }

    public static boolean moveFile2Dir(File srcFile, File destDir) {
        return moveFile(srcFile, new File(destDir, srcFile.getName()));
    }

    public static void ensureParentDirExist(File dir) {
        dir = dir.getParentFile();
        if (!dir.exists())
            dir.mkdirs();
    }

    public static void ensureDirExist(File dir) {
        if (!dir.exists())
            dir.mkdirs();
    }

    /**
     * 确保node的路径是存在的：目录就创建目录，文件则创建上层目录
     *
     * @param file
     */
    public static void ensurePathExist(File file) {
        if (!file.isDirectory())
            file = file.getParentFile();

        if (!file.exists())
            file.mkdirs();
    }

    public static void deleteDir(String dir) {
        deleteDir(new File(dir));
    }

    public static void deleteDir(File file) {
        if (!file.exists()) {
            return;
        }
        if (file.isFile()) {
            file.delete();
            return;
        }

        if (file.isDirectory()) {
            File[] childFiles = file.listFiles();
            if (childFiles == null || childFiles.length == 0) {
                file.delete();
                return;
            }

            for (int i = 0; i < childFiles.length; i++) {
                deleteDir(childFiles[i]);
            }
            file.delete();
        }
    }

    public static boolean createTextFile(String path, String text, String charsetName) {
        return createTextFile(new File(path), text, charsetName);
    }

    public static boolean createTextFile(File file, String text, String charsetName) {
        ensurePathExist(file);
        if (file.exists())
            file.delete();
        try (FileOutputStream fos = new FileOutputStream(file)) {
            file.createNewFile();
            fos.write(text.getBytes(charsetName));
            return true;
        } catch (IOException e) {
            return false;
        }
    }

    public static void copyPath(File src, File dest) throws IOException {
        if (src.isDirectory()) {
            ensureDirExist(dest);
            for (String file : src.list())
                copyPath(new File(src, file), new File(dest, file));
        } else {
            copyFile(src, dest);
        }
    }

    public static boolean copyFile(String srcFilePath, String destFilePath) {
        return copyFile(new File(srcFilePath), new File(destFilePath));
    }

    public static boolean copyFile(File srcFile, File destFile) {
        if (!isFileExist(srcFile)) {
            return false;
        }
        FsUtil.ensurePathExist(destFile);

        FileInputStream is = null;
        FileOutputStream os = null;
        FileChannel inputChannel = null;
        FileChannel outputChannel = null;
        try {
            is = new FileInputStream(srcFile);
            os = new FileOutputStream(destFile);
            inputChannel = is.getChannel();
            outputChannel = os.getChannel();
            outputChannel.transferFrom(inputChannel, 0, inputChannel.size());
            return true;
        } catch (IOException ex) {
            return false;
        } finally {
            closeIO(inputChannel);
            closeIO(outputChannel);
            closeIO(is);
            closeIO(os);
        }
    }

    public static boolean isDirExist(String filePath) {
        File file = new File(filePath);
        return (file.exists() && file.isDirectory());
    }

    public static boolean isFileExist(File file) {
        return (file.exists() && file.isFile());
    }

    public static boolean isFileExist(String filePath) {
        File file = new File(filePath);
        return (file.exists() && file.isFile());
    }

    public static boolean isPathExist(String filePath) {
        File file = new File(filePath);
        return file.exists();
    }

    public static void delFile(String filePath) {
        File file = new File(filePath);
        if (file != null && file.isFile())
            file.delete();
    }

    public static void string2File(String text, String filePath) {
        try (FileOutputStream out = new FileOutputStream(filePath)) {
            byte[] bytes = text.getBytes("UTF-8");
            out.write(bytes);
            out.flush();
        } catch (Exception e) {
        }
    }

    public static byte[] readFile(String filePath) {
        return readFile(new File(filePath));
    }

    public static byte[] readFile(File file) {
        byte[] b = null;
        FileInputStream in = null;
        try {
            b = new byte[(int) file.length()];
            in = new FileInputStream(file);
            in.read(b);
        } catch (IOException e) {
            log.error("read file error", e);
        } finally {
            closeIO(in);
        }
        return b;
    }

    public static List<String> readTxtFileLines(String filePath) {
        return readTxtFileLines(filePath, "UTF-8");
    }

    public static List<String> readTxtFileLines(String filePath, String encoding) {
        List<String> list = new ArrayList<String>();
        File file = new File(filePath);
        if (file.isFile() && file.exists()) { // 判断文件是否存在
            try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(new FileInputStream(file), encoding))) {
                String lineTxt = null;
                while ((lineTxt = bufferedReader.readLine()) != null) {
                    list.add(lineTxt);
                }
            } catch (Exception e) {
                log.error("readTxtFileLines", e);
            }
        } else {
            log.error("file not exist");
        }

        return list;
    }

    /**
     * @param path         父路径
     * @param listFilePath 相对路径列表
     * @return
     */
    public static Map<String, Boolean> mergePathList(File path, String listFilePath) {
        Map<String, Boolean> matchMap = Maps.newHashMap();
        if (Strings.isNullOrEmpty(listFilePath))
            return matchMap;

        List<String> sl = FsUtil.readTxtFileLines(listFilePath);
        log.info("total :" + sl.size());

        for (String filePath : sl) {
            File file = new File(path, filePath);
            matchMap.put(file.getAbsolutePath(), true);
        }

        return matchMap;
    }

    // opt/xxx，没有最后/
    public static String genPath(String path, String name) {
        return String.format("%s%s%s", path, Separator, name);
    }

    // opt/xxx/yyy，没有最后/
    public static String genPath(String path, String name1, String name2) {
        return String.format("%s%s%s%s%s", path, Separator, name1, Separator, name2);
    }

    // opt/xxx/yyy，没有最后/
    public static String genFilePath(String path, String name1, String name2, String name3) {
        return String.format("%s%s%s%s%s%s%s", path, Separator, name1, Separator, name2, Separator, name3);
    }

    // opt/xxx/yyy，没有最后/
    public static String genFilePath(String path, String name1, String name2, String name3, String name4) {
        return String.format("%s%s%s%s%s%s%s%s%s", path, Separator, name1, Separator, name2, Separator, name3, Separator, name4);
    }

    // opt/xxx/yyy/，有最后/
    public static String genFullPath(String path, String name1, String name2) {
        return String.format("%s%s%s%s%s", path, Separator, name1, Separator, name2, Separator);
    }

    // opt/xxx/，有最后/
    public static String genFullPath(String path, String name) {
        return String.format("%s%s%s%s", path, Separator, name, Separator);
    }

    // opt/，有最后/
    public static String genFullPath(String path) {
        return String.format("%s%s", path, Separator);
    }

    public static File changeExtension(File f, String newExtension) {
        int i = f.getName().lastIndexOf('.');
        String name = f.getName().substring(0, i);
        return new File(f.getParent() + "/" + name + "." + newExtension);
    }

    // 是否全是文件夹
    public static boolean isAllDir(File dir) {
        for (File file : dir.listFiles()) {
            if (!file.isDirectory()) {
                return false;
            }
        }
        return true;
    }

    /*
     * 获取dir目录下的所有文件的绝对路径List（包括子文件夹下的文件）
     */
    public static List<String> getFilePathList(File dir) {
        List<String> filePaths = new ArrayList<String>();
        LinkedList<File> list = new LinkedList<File>();

        for (File file : dir.listFiles()) {
            if (file.isDirectory()) {
                // 把第一层的目录，全部放入链表
                list.add(file);
            } else {
                filePaths.add(file.getAbsolutePath());
            }
        }
        // 循环遍历链表
        while (!list.isEmpty()) {
            // 把链表的第一个记录删除
            File tmp = list.removeFirst();
            if (tmp.isDirectory()) {
                for (File file : tmp.listFiles()) {
                    if (file.isDirectory()) {
                        // 如果遍历到的是目录，则将继续被加入链表
                        list.add(file);
                    } else {
                        filePaths.add(file.getAbsolutePath());
                    }
                }
            }
        }
        return filePaths;
    }

    /*
     * 获取dir目录下的所有文件数量（包括子文件夹下的文件）
     */
    public static int getFileCount(File dir) {
        return getFilePathList(dir).size();
    }

    public static List<String> readTxtFileLines(InputStream inputStream) {
        List<String> list = new ArrayList<String>();
        try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"))) {
            String lineTxt = null;
            while ((lineTxt = bufferedReader.readLine()) != null) {
                list.add(lineTxt);
            }
        } catch (Exception e) {
            log.error("读取文件内容出错");
        }

        return list;
    }

    public static String readTextFile(String path) {
        return readTextFile(new File(path));
    }

    public static String readTextFile(File file) {
        if (!file.exists())
            return null;
        try (FileInputStream fis = new FileInputStream(file)) {
            byte[] buffer = new byte[fis.available()];
            fis.read(buffer);

            return EncodingUtil.decodeUTF8(buffer);
        } catch (IOException e) {
            return null;
        }
    }

    public static boolean writeFile(File destFile, byte[] data) {
        try {
            Files.write(data, destFile);
            return true;
        } catch (IOException e) {
            return false;
        }
    }

    public static boolean writeTxtFile(File destFile, String text) {
        try {
            FsUtil.ensurePathExist(destFile);
            Files.write(text, destFile, EncodingUtil.UTF8_CHARSET);
            return true;
        } catch (IOException e) {
            return false;
        }
    }

    public static void closeIO(Closeable c) {
        try {
            if (c != null) {
                c.close();
            }
        } catch (IOException e) {
            log.error("关闭流失败");
        }
    }

    public static Boolean mkdirIfNotExist(String path) {
        File dir = new File(path);
        boolean mkdir;
        if (!dir.exists()) {
            mkdir = dir.mkdirs();
        } else {
            mkdir = false;
        }
        return mkdir;
    }

    public static String getFileType(String fileName) {
        return fileName.substring(fileName.lastIndexOf("."));
    }

    /**
     * 获取目录下的所有子目录
     */
    public static List<String> getChildDirectory(File file, List<String> resultFileName) {
        File[] files = file.listFiles();
        if (files == null)
            return resultFileName;// 判断目录下是不是空的
        for (File f : files) {
            if (f.isDirectory()) {// 判断是否文件夹
                resultFileName.add(f.getPath());
                getChildDirectory(f, resultFileName);// 调用自身,查找子目录
            }
        }
        return resultFileName;
    }

    /**
     * 读取一个文件,返回 String 的文件内容
     *
     * @param absolutePath
     * @param charset
     * @return
     */

    public static String readFile(String absolutePath, String charset) {

        File f = new File(absolutePath);
        if (!f.isFile()) {
            if (f.exists()) {
                throw new RuntimeException(new StringBuilder(absolutePath).append(" isn't file").toString());
            } else {
                throw new RuntimeException(new StringBuilder(absolutePath).append(" isn't exist").toString());
            }
        }
        if (f.length() > FILE_MAX) {
            throw new RuntimeException(
                    new StringBuilder(absolutePath).append(" too big,").append(f.length() / (1024 * 1024)).append("MB")
                            .append(" FILE_MAX=").append(FILE_MAX / (1024 * 1024)).append("MB").toString());
        }
        FileInputStream fis = null;
        InputStreamReader isr = null;

        try {
            fis = new FileInputStream(f); // 读取原文件
            int lenght = (int) f.length(); // 文件字符总数
            if (StringUtils.isBlank(charset)) {
                isr = new InputStreamReader(fis);
            } else {
                isr = new InputStreamReader(fis, charset);
            }
            char[] charArr = new char[lenght];
            isr.read(charArr, 0, lenght);
            return new String(charArr); // 把文件内容转换为字符串
        } catch (Exception e) {
            throw new RuntimeException(e.getMessage(), e);
        } finally {
            try {
                if (isr != null) {
                    isr.close();
                }
            } catch (Exception e) {
                if (log.isDebugEnabled()) {
                    log.error(e.getMessage(), e);
                }
            } finally {
                try {
                    if (fis != null) {
                        fis.close();
                    }
                } catch (Exception e) {
                    if (log.isDebugEnabled()) {
                        log.error(e.getMessage(), e);
                    }
                }
            }
        }
    }

    public static Boolean download(String url, File toFile) {
        try {
            downloadFile(url, toFile.getPath(), 3000, 600000);
            return true;
        } catch (IOException e) {
            log.error(LogUtil.genException(e, "download fail", "url", url), e);
            return false;
        }
    }

    public static void downloadFile(String fileUrl, String fileName, int connectTimeoutMills, int readTimeoutMills) throws IOException {
        File parentFile = new File(fileName).getParentFile();
        if (!parentFile.exists()) {
            parentFile.mkdirs();
        }
        URL url = new URL(fileUrl);
        HttpURLConnection con = (HttpURLConnection) url.openConnection();
        // 设置超时间 单位毫秒
        con.setConnectTimeout(connectTimeoutMills);
        con.setReadTimeout(readTimeoutMills);

        InputStream is = null;
        FileOutputStream os = null;
        BufferedOutputStream bos = null;
        GZIPInputStream gis = null;
        try {
            is = con.getInputStream();// 输入流
            os = new FileOutputStream(fileName);
            bos = new BufferedOutputStream(os);
            int bufferSize = 8192;
            String code = con.getHeaderField("Content-Encoding");
            if ((null != code) && code.equals("gzip")) {
                gis = new GZIPInputStream(is);
                // 8K的数据缓冲
                byte[] bs = new byte[bufferSize];
                // 读取到的数据长度
                int len;
                // 输出的文件流
                bos = new BufferedOutputStream(new FileOutputStream(fileName));
                // 开始读取
                while ((len = gis.read(bs)) != -1) {
                    bos.write(bs, 0, len);
                }

            } else {
                // 8K的数据缓冲
                byte[] bs = new byte[bufferSize];
                // 读取到的数据长度
                int len;
                // 输出的文件流
                // 开始读取
                while ((len = is.read(bs)) != -1) {
                    bos.write(bs, 0, len);
                }
            }
        } finally {
            // 完毕，关闭所有链接
            if (null != gis) {
                try {
                    gis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (null != bos) {
                try {
                    bos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (null != os) {
                try {
                    os.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (null != is) {
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }


    /**
     * 判断输出目录是否为输入目录的子目录
     *
     * @param inDir
     * @param outDir
     * @return
     */
    public static boolean vaildateIfChildDir(File inDir, File outDir) {
        int i = inDir.getAbsolutePath().lastIndexOf(File.separator);
        int j = outDir.getAbsolutePath().lastIndexOf(File.separator);
        return outDir.getAbsolutePath().startsWith(inDir.getAbsolutePath()) && (i != j);
    }

    public static String getFileNameSuffix(String filePath) {
        int fromIndex = filePath.lastIndexOf(CommonConstant.DEFAULT_FILE_SEPARATOR) + 1;
        return filePath.substring(fromIndex);
    }

    public static String getFileTypeByFilePath(String resourcePath) {
        String niigz = ".nii.gz";
        if (resourcePath.endsWith(niigz)) {
            return niigz.substring(1);
        }
        return resourcePath.substring(resourcePath.lastIndexOf(CommonConstant.FILE_NAME_SEPARATOR) + 1);
    }

    public static String getFileName(String filePath) {
        int fromIndex = filePath.lastIndexOf(CommonConstant.DEFAULT_FILE_SEPARATOR) + 1;
        int endIndex = filePath.lastIndexOf(CommonConstant.FILE_NAME_SEPARATOR);
        return filePath.substring(fromIndex, endIndex);
    }


}